package com.qhong.spring.impl;

import com.qhong.spring.ApplicationContext;
import com.qhong.spring.Context;
import com.qhong.spring.annotation.Autowired;
import com.qhong.spring.annotation.Bean;
import com.qhong.spring.annotation.ComponentScan;
import com.qhong.spring.annotation.Configuration;
import com.qhong.spring.annotation.stereotype.Component;
import com.qhong.spring.annotation.stereotype.Controller;
import com.qhong.spring.annotation.stereotype.Repository;
import com.qhong.spring.annotation.stereotype.Service;
import com.qhong.spring.utils.NamingUtils;
import com.qhong.spring.utils.PackageUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 容器操作的类抽象类，将公用的代码写这里
 *
 * @author ranger
 *
 */
public abstract class AbstractApplicationContext implements ApplicationContext {
	// 声明一个线程变量，存储容器对象，表示同一条线程，一个ApplicationContext只操作一个容器对象。
	private static Context context = new ContextImpl();

	protected String[] basePackage = null;

	/**
	 * 将容器操作加载创建对象的代码写抽象类里面，这样可以方便以后扩展多种实现。
	 *
	 * @param classType
	 */
	public AbstractApplicationContext(Class<?> classType) {
		try {
			// 判断配置类是否有Configuration注解
			Configuration annotation = classType.getDeclaredAnnotation(Configuration.class);
			if(annotation!=null){
				bean(classType, this.getContext());
				scan(classType, this.getContext());
			}

			// 1.替换掉可以使用构造函数注入的对象
			//autowiredConstructor();
			// 注入对象到有@Autowired注解属性和方法中。
			autowired();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public String[] getBasePackage() {
		return basePackage;
	}

	/**
	 * 获得容器，容器是一个线程安全的对象
	 *
	 * @return
	 */
	public Context getContext() {

		return context;
	}

	/**
	 * 实现从容器通过对象名，获得对象
	 */
	@Override
	public Object getBean(String objectName) {
		// 1.获得容器中的所有对象
		Map<String, Object> objects = this.getContext().getObjects();
		// 2.通过Key获得容器中的对象
		Object object = objects.get(objectName);
		// 3.如果对象不为空，返回对象
		if (object != null) {
			return object;
		}
		return null;
	}

	@Override
	public <T> T getBean(String objectName, Class<T> className) {
		// 1.获得容器中的所有对象
		Map<String, Object> objects = this.getContext().getObjects();
		// 2.通过Key获得容器中的对象
		Object object = objects.get(objectName);
		// 3.如果对象不为空，,判断对象是否与传入的类类型的父接口或类类型兼容，返回对象
		if (object != null && className.isAssignableFrom(object.getClass())) {
			return (T) object;
		}
		return null;
	}

	/**
	 * 判断是否是组件
	 *
	 * @param classType
	 * @return
	 */
	private boolean isComponent(Class<?> classType) {
		// 如果是接口，就不能创建对象，直接返回false
		if (classType.isInterface()) {
			return false;
		}
		Component component = classType.getDeclaredAnnotation(Component.class);
		Service service = classType.getDeclaredAnnotation(Service.class);
		Controller controller = classType.getDeclaredAnnotation(Controller.class);
		Repository repository = classType.getDeclaredAnnotation(Repository.class);
		// 判断只要有一个组件注解,就返回
		if (component != null || service != null || controller != null || repository != null) {
			return true;
		}
		return false;
	}

	/**
	 * 获得组件的对象名
	 *
	 * @param classType
	 * @return
	 */
	private String getComponentOfName(Class<?> classType) {
		// 获得四个组件注解的对象
		Component component = classType.getDeclaredAnnotation(Component.class);
		Service service = classType.getDeclaredAnnotation(Service.class);
		Controller controller = classType.getDeclaredAnnotation(Controller.class);
		Repository repository = classType.getDeclaredAnnotation(Repository.class);
		// 判断注解对象是否为空，注解对象的的name属性是否有值
		if (component != null) {
			if (!"".equals(component.name()) && component.name() != null) {
				return component.name();
			}
		}
		if (service != null) {
			if (!"".equals(service.name()) && service.name() != null) {
				return service.name();
			}
		}
		if (controller != null) {
			if (!"".equals(controller.name()) && controller.name() != null) {
				return controller.name();
			}
		}
		if (repository != null) {
			if (!"".equals(repository.name()) && repository.name() != null) {
				return repository.name();
			}
		}
		return null;

	}

	/**
	 * 给对象的属性注入关联的对象
	 *
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private void autowired() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {

		// 2.获得容器中的所有对象。
		Map<String, Object> objects = context.getObjects();
		// 3.获得容器中所有的对象值
		Collection<Object> values = objects.values();
		// 4.获得对象的迭代器
		Iterator<Object> iterator = values.iterator();
		while (iterator.hasNext()) {
			Object object = iterator.next();
			// 1.注入到属性
			this.autowiredField(object, context);
			// 2.注入到方法
			this.autowiredMethod(object, context);
		}
	}

	private void bean(Class<?> classType,Context context) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		Map<String, Object> objects = context.getObjects();
		//1.获得配置类的对象
		Object configuration = classType.newInstance();
		//2.获得配置类的所有方法
		Method[] methods = classType.getMethods();
		for(Method method:methods){
			Bean bean = method.getDeclaredAnnotation(Bean.class);
			if(bean!=null){
				Object object = method.invoke(configuration);
				if(bean.name()!=null&&!"".equals(bean.name())){
					objects.put(bean.name(), object);
				}else{
					objects.put(NamingUtils.firstCharToLower(object.getClass().getSimpleName()), object);
				}
			}
		}

	}

	private void scan(Class<?> classType,Context context) throws ClassNotFoundException, InstantiationException, IllegalAccessException{

		// 获得组件扫描注解
		ComponentScan componentScan = classType.getDeclaredAnnotation(ComponentScan.class);
		// 获得包名
		this.basePackage = componentScan.basePackages();
		// 根据包名获得类全限制名
		// Set<String> classNames =
		// PackageUtils.getClassName(this.basePackage[0], true);
		// 将扫描一个包，修改为多个包
		Set<String> classNames = PackageUtils.getClassNames(this.basePackage, true);
		// 通过类名创建对象
		Iterator<String> iteratorClassName = classNames.iterator();
		while (iteratorClassName.hasNext()) {

			String className = iteratorClassName.next();
			// System.out.println(className);

			// 通过类全名创建对象
			Class<?> objectClassType = Class.forName(className);
			/*
			 * 判断如果类权限名对应的不是接口，并且包含有@Component|@Controller|@Service|
			 *
			 * @Repository 才可以创建对象
			 */
			if (this.isComponent(objectClassType)) {
				Object instance = objectClassType.newInstance();
				// 修改为,默认对象支持首字符小写
				String objectName = null;
				// 获得组件注解的name属性值
				String componentName = this.getComponentOfName(objectClassType);

				if (componentName == null) {
					// 如果组件注解的name属性没有值，使用默认命名对象
					objectName = NamingUtils.firstCharToLower(instance.getClass().getSimpleName());
				} else {
					// 如果组件注解的name属性有值，使用自定义命名对象
					objectName = componentName;
				}
				System.out.println("1."+instance);
				//将对象加入到容器
				context.addObject(objectName, instance);
			}

		}
	}


	/**
	 * 属性注入
	 *
	 * @param object
	 * @param context
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	private void autowiredField(Object object, Context context)
			throws IllegalArgumentException, IllegalAccessException {
		// 5.获得对象的表结构
		Class<? extends Object> classType = object.getClass();
		// 6.获得字段的结构
		Field[] fields = classType.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			// autowired获得注解
			Autowired autowired = fields[i].getAnnotation(Autowired.class);
			if (autowired != null) {
				Class<?> fieldType = fields[i].getType();
				String fieldName = fields[i].getName();
				// 如果容器里面有对应的对象
				Object fieldObject = context.getObject(fieldType, fieldName);
				// 允许访问私有方法
				if (fieldObject != null) {
					// 属性是私有的也可以访问
					fields[i].setAccessible(true);
					// 将属性值赋予这个对象的属性
					fields[i].set(object, fieldObject);
				}

			}
		}
	}

	/**
	 * 注意set方法的规范 限定方法只能注入一个参数，并且注入对象的方法只能有一个参数
	 *
	 * @param object
	 * @param context
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private void autowiredMethod(Object object, Context context)
			throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		// 5.获得对象的表结构
		Class<? extends Object> classType = object.getClass();
		// 6.获得类方法的结构
		Method[] methods = classType.getDeclaredMethods();
		for (int i = 0; i < methods.length; i++) {
			// autowired获得注解
			Autowired autowired = methods[i].getAnnotation(Autowired.class);
			if (autowired != null) {
				// 获得参数列表的类型
				Class<?>[] parameterTypes = methods[i].getParameterTypes();

				String fieldName = methods[i].getName();

				// 如果容器里面有对应的对象，限定一个方法只能注入一个对象
				Object methodObject = context.getObject(parameterTypes[0], fieldName);
				// 允许访问私有方法
				if (methodObject != null) {
					// 属性是私有的也可以访问
					methods[i].setAccessible(true);
					// 将属性值赋予这个对象的属性
					methods[i].invoke(object, methodObject);
				}

			}
		}
	}

	/**
	 * TODO 未完成
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws InstantiationException

	private void autowiredConstructor() throws IllegalArgumentException, IllegalAccessException,
	InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException {
	Context context=this.getContext();
	Map<String, Object> objects = context.getObjects();

	Set<Entry<String, Object>> entrySet = objects.entrySet();
	Iterator<Entry<String, Object>> iterator = entrySet.iterator();
	while (iterator.hasNext()) {
	Entry<String, Object> next = iterator.next();

	Constructor<?>[] constructors = next.getValue().getClass().getConstructors();
	for (int i = 0; i < constructors.length; i++) {
	Class<?>[] parameterTypes = constructors[i].getParameterTypes();
	// 只能注入一个参数的构造函数
	if (parameterTypes.length == 1) {

	// autowired获得注解
	Autowired autowired = constructors[i].getAnnotation(Autowired.class);
	if (autowired != null) {

	String constructorName = parameterTypes[0].getName();

	// 如果容器里面有对应的对象，限定一个方法只能注入一个对象
	Object methodObject = context.getObject(parameterTypes[0], constructorName);
	// 私有方法也可以赋值
	constructors[i].setAccessible(true);
	// 使用指定构造参数的创建一个新对象

	Object newInstance = constructors[i].newInstance(methodObject);

	//覆盖原来的对象，使用使用@Autowired注入构造方法的对象
	System.out.println("2."+newInstance);
	//覆盖前，将原来对象的赋予新的对象
	Object object = next.getValue();
	Field[] fields = object.getClass().getFields();


	objects.put(next.getKey(), newInstance);

	}
	break;
	}
	}
	}

	}
	 */

}